import { Injectable } from '@nestjs/common';
import { DbService } from '../../db/db.service';
import { User } from '@app/db/entity/User';
import { UserService } from '../user/user.service';
import { ConfigureService } from '@app/configure';
import { JwtVerifyService } from '@app/jwt-verify';
import { CacheService } from '@app/cache';
import { HttpService } from '@nestjs/axios';
import { randomUUID } from 'crypto';
import WorkException from 'exception/work.exception';
import { Api } from 'utils/TLSSigAPIv2';
import { SmsService } from '../../sms/sms.service';
import { ImclientService } from '@app/imclient';
import { CommonService } from '../common/common.service';



@Injectable()
export class LoginService {
    private jwtExpire = 0
    private regsmscodekey = 'reg:sms:'
    private resendexpirekey = 'rs:sms:'
    private resendexpire = 60

    constructor(
        private readonly db:DbService,
        private readonly config:ConfigureService,
        private readonly jwt:JwtVerifyService,
        private readonly cache:CacheService,
        private readonly http:HttpService,
        private readonly userService:UserService,

        private readonly common:CommonService,

        private readonly imclient:ImclientService,


        private readonly smsService:SmsService,

    ) {
        this.jwtExpire = Number(this.config.get('JWT_EXPIRE'))
    }

    private async addofficial(id:string) {
        const users = await this.common.officialUsers()
        if(users) {
            for(let k in users) {
                this.imclient.addFriends(id, users[k].value)
            }
        }
    }

    async generateAccountNumber(userId: number, time:number, position: 'prepend' | 'append' | 'middle' = 'append'): Promise<string> {

        const r = await this.cache.client.lpop('account_number_queue')
        if(r) {
            return r
        }else {
            return String(userId)
        }



        const shortNow = String(time).slice(2); // 去掉前两位，得到后8位时间戳
      
        // 反转 userId 部分
        const suffix = String(userId).padStart(5, '0'); // 保证 userId 长度为 5 位
        const reversedSuffix = suffix.split('').reverse().join(''); // 反转 userId
        
        // 根据位置插入 reversedSuffix
        let accountNumber = '';
        if (position === 'prepend') {
          accountNumber = `${reversedSuffix}${shortNow}`;
        } else if (position === 'append') {
          accountNumber = `${shortNow}${reversedSuffix}`;
        } else if (position === 'middle') {
          const middleIndex = Math.floor(shortNow.length / 2);
          accountNumber = `${shortNow.slice(0, middleIndex)}${reversedSuffix}${shortNow.slice(middleIndex)}`;
        }
      
        return accountNumber;
      }

    //获取注册验证码重发时间
    public getRegisterSmsResendExpire(phone:string) {
        return this.cache.client.ttl('register:resend:expire:' + phone)
    }

    //发送注册的的验证码
    public async sendRegisterSms(phone:string) {
        const regex = /^1\d{10}$/;
        if(!regex.test(phone)) throw new WorkException('手机号码不正确')

        const key = 'register:' + phone

        const lockKey = 'reg:sms::lock' + phone
        const lock = await this.cache.set(lockKey, '', 10, 'NX')
        if(!lock) return null

        const user = await this.db.user.findOne({phone: phone}, {id:true})
        if(user != null) {
            this.cache.del(lockKey)
            throw new WorkException('该手机号已经注册')
        }

        try {

            const code = await this.smsService.send(phone)
            this.cache.set(key, code, 5*60)
            this.cache.set('register:resend:expire:' + phone, '1', this.resendexpire)

            return this.resendexpire
            
        } catch (error) {
            
        }finally {
            this.cache.del(lockKey)
        }


        return null
    }

    //注册
    public async register(phone:string, code:string, name:string) {
        const lockKey = 'register:lock:' + phone
        const lock = await this.cache.set(lockKey, '', 10, 'NX')
        //if(!lock) return false

        const key = 'register:' + phone

        const c = await this.cache.get(key)
        if(c == null) {
            this.cache.del(lockKey)
            throw new WorkException('验证码已过期')
        }
        if(c != code) {
            this.cache.del(lockKey)
            throw new WorkException('验证码错误')
        }

        // const count = await this.db.user.count()
        // if(count >= 90) {
        //     throw new WorkException('网络连接失败')
        // }


        const defaultAvatars = this.config.get('upload.defaultAvatars')

        let userId = 0
        let imUserId = ''
        

        const time = Math.floor(Date.now() / 1000)
        const user = await this.db.user.findOne({phone: phone},{id:true, registerTime:true, imUserId: true})
        if(user == null) {
            const r = await this.db.user.insert({phone: phone, registerTime: time})
            if(!r) throw new WorkException('注册失败')
            userId = r.rows[0][this.db.user.primaryKey]
            imUserId = await this.generateAccountNumber(userId, time, 'middle')
        }else {

            if(user.atIm) {
                throw new WorkException('手机号已被注册')
            }

            imUserId = await this.generateAccountNumber(userId, user.registerTime, 'middle')

        }

        const avatar = defaultAvatars[userId%10]

        let userSig = ''

        let regiserData:any = {avatar: this.config.get('upload.staticURL') + avatar}
        if(name) {
            regiserData.name = name
        }

        const res = await this.imclient.createUser(imUserId, regiserData)
        if(res.code == 200) {
            this.db.user.update({id: userId}, {atIm: 1, imUserId: imUserId, avatar: avatar, name: name || ''})
            userSig = res.data.token

            this.addofficial(imUserId)
        }else {
            throw new WorkException('注册失败')
        }

       
        const accessToken = await this.makeToken(userId, time)

    
        const result:any = {
            hadpwd: false,
            sdkappid: this.config.get('im.AppKey'),
            accessToken: accessToken,
            expire: this.jwtExpire,
            avatar: '',
            name: '',
            userId: imUserId,
            userSig: userSig
        }

        return result

    }

    //获取短信登录验证重发有效期
    public getLogoinSmsResendExpire(phone:string) {
        return this.cache.client.ttl('login:resend:expire:' + phone)
    }

    //发送登录验证码
    public async sendLoginSms(phone:string) {
        const regex = /^1\d{10}$/;
        if(!regex.test(phone)) throw new WorkException('手机号码不正确')

        const key = 'login:' + phone

        const lockKey = 'login:sms::lock' + phone
        const lock = await this.cache.set(lockKey, '', 10, 'NX')
        if(!lock) return null

        const user = await this.db.user.findOne({phone: phone}, {id:true})
        if(user == null) {
            this.cache.del(lockKey)
            throw new WorkException('该手机号未注册')
        }

        try {

            const code = this.smsService.send(phone)
            this.cache.set(key, code, 5*60)
            this.cache.set('login:resend:expire:' + phone, '1', this.resendexpire)

            return this.resendexpire
            
        } catch (error) {
            
        }finally {
            this.cache.del(lockKey)
        }


        return null
    }

    //短信登录
    public async loginBySms(phone:string, code:string) {
        const lockKey = 'login:lock:' + phone
        const lock = await this.cache.set(lockKey, '', 10, 'NX')
        if(!lock) return false

        const key = 'login:' + phone

        const c = await this.cache.get(key)
        if(c == null) {
            this.cache.del(lockKey)
            throw new WorkException('验证码已过期')
        }
        if(c != code) {
            this.cache.del(lockKey)
            throw new WorkException('验证码错误')
        }

        const user = await this.db.user.findOne({phone: phone},{enable:true, sign:true, birth: true, id:true, avatar:true, name:true, atIm: true, password:true, registerTime:true, imUserId:true})
        if(user == null) {
            throw new WorkException('手机号未注册')
        }

        
        const result = await this.loginHandle(user)

        result.hadpwd = user.password == '' ? false : true

        return result

    }

    //密码登录
    public async login(phone:string, password:string) {
        const user = await this.db.user.findOne({phone: phone}, {enable:true, sign:true, birth: true, id:true, atIm:true, password:true, salt:true, name:true, avatar: true, registerTime:true, imUserId:true})
        if(!user) throw new WorkException('手机号或密码错误')

        if(this.userService.buildPassword(password, user.salt) != user.password) {
            throw new WorkException('手机号或密码错误')
        }

        return this.loginHandle(user)
    }

    async loginHandle(user:User) {
        const userId = user.id
        let imUserId = user.imUserId //用户在腾讯的userid

        const time = Math.floor(Date.now() / 1000)

        
        let userSig = ''

        if(!user.enable) {
            throw new WorkException('你已严重违反信息安全聊天管理条例，账号已被封禁!')
        }


        if(user.atIm) {
            
            const res = await this.imclient.refreshToken(imUserId)

            if(res.code == 200) {
                userSig = res.data.token
            }else {
                throw new WorkException('登录失败')
            }

        }else {
            imUserId = await this.generateAccountNumber(user.id, user.registerTime, 'middle')

            const res = await this.imclient.createUser(imUserId, {name: user.name})
            if(res.code == 200) {
                userSig = res.data.token
                this.db.user.update({id: user.id}, {atIm: 1, imUserId: imUserId})

                this.addofficial(imUserId)
            }else {
                throw new WorkException('登录失败')
            }
        }
       
        const accessToken = await this.makeToken(userId, time)


        const result:any = {
            hadpwd: true,
            sdkappid: this.config.get('im.AppKey'),
            accessToken: accessToken,
            expire: this.jwtExpire,
            avatar: user.avatar,
            name: user.name,
            userId: imUserId,
            userSig: userSig,
            sign: user.sign,
            birth: user.birth,
        }

        return result
        
    }


    //获取忘记密码验证码重发时间
    public getforgetResendExpire(phone:string) {
        return this.cache.client.ttl('forget:resend:expire:' + phone)
    }
    //发送忘记密码验证码
    public async sendForgetSms(phone:string) {
        const regex = /^1\d{10}$/;
        if(!regex.test(phone)) throw new WorkException('手机号码不正确')

        const key = 'forget:pwd:' + phone

        const lockKey = 'forget:sms::lock' + phone
        const lock = await this.cache.set(lockKey, '', 60, 'NX')
        if(!lock) return null

        const user = await this.db.user.findOne({phone: phone}, {id:true})
        if(user == null) {
            this.cache.del(lockKey)
            throw new WorkException('该手机号未注册')
        }

        try {

            const code = await this.smsService.send(phone)
            this.cache.set(key, code, 5*60)
            this.cache.set('forget:resend:expire:' + phone, '1', this.resendexpire)

            return this.resendexpire
            
        } catch (error) {
            
        }finally {
            this.cache.del(lockKey)
        }


        return null
    }


    public async forgetcheck(phone:string, code:string) {
        const key = 'forget:pwd:' + phone
        const c = await this.cache.get(key)
        if(c == null) {
           // this.cache.del(lockKey)
            throw new WorkException('验证码已过期')
        }
        if(c != code) {
            //this.cache.del(lockKey)
            throw new WorkException('验证码错误')
        }

        const str = randomUUID()

        this.cache.set('forget:key:' + phone, str, 5*60)

        return str
    }

    public async resetPassword(phone, nonce, password:string) {
        const key = 'forget:key:' + phone
        const str = await this.cache.get(key)
        if(str == null) {
           // this.cache.del(lockKey)
            throw new WorkException('已过期，请重新获取验证码')
        }
        if(str != nonce) {
            throw new WorkException('重置失败，已过期，请重新获取验证码')
        }

        const user = await this.db.user.findOne({phone: phone}, {id:true})
        if(!user) return  false

        const salt = this.userService.generateSalt()
        password = this.userService.buildPassword(password, salt)

        const r = await this.db.user.update({id: user.id}, {password: password, salt: salt})

        return r == 1
    }


    public async refresh(userId:number) {
        const user = await this.db.user.findOne({id: userId}, {id: true})
        if(!user) return null

        const accessToken = await this.makeToken(userId, Math.floor(Date.now() / 1000))

        return {
            expire : this.jwtExpire,
            accessToken
        }
    }

    private async makeToken(id:number, time:number) {
        return this.jwt.sign({key : id, id : Math.ceil((time - 1030) / 263), time : time}, this.jwtExpire)
    }





    public async register1(phone:string, code:string) {
        const lockKey = 'register:' + phone
        const lock = await this.cache.set(lockKey, '', 10, 'NX')
        if(!lock) return false

        const key = this.regsmscodekey + phone

        const c = await this.cache.get(key)
        if(c == null) {
            this.cache.del(lockKey)
            throw new WorkException('验证码已过期')
        }
        if(c != code) {
            this.cache.del(lockKey)
            throw new WorkException('验证码错误')
        }

        let userId = 0
        let isRegister = false
        let needpwd = false

        const time = Math.floor(Date.now() / 1000)
        const user = await this.db.user.findOne({phone: phone},{id:true, avatar:true, name:true, atIm: true, password:true})
        if(user == null) {
            const r = await this.db.user.insert({phone: phone, registerTime: time})
            if(!r) throw new WorkException('注册失败')

            isRegister = true
            needpwd = true
    
            userId = r.rows[0][this.db.user.primaryKey]
        }else {
            userId = user.id

            if(!user.atIm) {
                isRegister = true
            }

            if(user.password == '') {
                needpwd = true
            }
        }

        
        
    
        
        const sdkappid = this.config.get('im.SDKAppID')
        const identifier = this.config.get('im.amdinUserId')

        const api = new Api(this.config.get('im.SDKAppID'), this.config.get('im.secretKey'))

        const txuserid = String(userId) //用户在腾讯的userid
        
        //还没注册到腾讯
        if(isRegister) {
            var usersig = api.genUserSig(identifier, 10000);
            const url = `https://console.tim.qq.com/v4/$$/?sdkappid=${sdkappid}&identifier=administrator&usersig=${usersig}&random=${time}&contenttype=json`

            const res = await this.http.axiosRef.post(url.replace('$$', 'im_open_login_svc/account_import'), {
                UserID: txuserid,
                "Nick":txuserid,
                "FaceUrl":""
            })

            if(res.status == 200 && res.data.ActionStatus == 'OK') {
                await this.http.axiosRef.post(url.replace('$$', 'profile/portrait_set'), {
                    From_Account: txuserid,
                    ProfileItem: [
                        {
                            "Tag":"Tag_Profile_IM_AllowType",
                            "Value":"AllowType_Type_NeedConfirm"
                        }
                    ]
                })
            }else {
                console.log('注册到腾讯失败')
                console.log(res.status)
                console.log(res.data)
                throw new WorkException('登录失败')
            }
        }
       
        const accessToken = await this.makeToken(userId, time)

    
        const result:any = {
            needpwd: needpwd,
            sdkappid: sdkappid,
            accessToken: accessToken,
            expire: this.jwtExpire,
            avatar: '',
            name: '',
            userId: txuserid,
            userSig: api.genUserSig(txuserid, this.jwtExpire)
        }


    
        return result

    }
}
